#pragma once
#include <iostream>
#include <memory>
#include <unordered_map>
#include "Epoller.hpp"
#include "Connection.hpp"

using namespace EpollModule;

using connection_t = std::shared_ptr<Connection>;

// 反应器：负责管理所有的连接（）
class Reactor
{
    const static int event_num = 64;

private:
    // 判断文件描述符为sockfd对应的链接是否在我们的连接队列中
    bool IsConnectionExists(int sockfd)
    {
        // 对于连接队列的数据结构是哈希表，因此我们直接根据sockfd来查找即可
        return _connections.find(sockfd) != _connections.end();
    }

public:
    // reactor的构造函数，其主要的工作就是创建一个epoll实例并完成初始化。
    Reactor() : _isrunning(false), _epoller(std::make_unique<Epoller>())
    {
        _epoller->Init();
    }

    // 向Reactor的连接队列中插入一个新连接
    void InsertConnection(connection_t conn)
    {
        // 这个思路也很简单。连接队列的本质是哈希表。所以我们的工作其实就是将conn直接插入到哈希表中
        // 同时也需要将该连接中需要内核关心的事件通过epoll_ctl告诉内核，
        // 这个过程被封装在了Epoller类的ADD函数中，我们只需要按照要求传入对应的参数调用即可
        auto iter = _connections.find(conn->Sockfd());
        if (iter == _connections.end())
        {
            // 1. 把连接，放到unordered_map中进行管理
            _connections.insert(std::make_pair(conn->Sockfd(), conn));
            // 2. 把新插入进来的连接，写透到内核的epoll中
            _epoller->Add(conn->Sockfd(), conn->GetEvents());
            // 3. 设置关联关系，让connection回指当前对象，让这个链接知道是哪个Reactor在管他
            conn->SetOwner(this);
            LOG(LogLevel::DEBUG) << "add connection success: " << conn->Sockfd();
        }
    }
    
    // 为其他模块提供一个修改连接队列中某个连接对应等待事件的接口。
    // 假如说某个连接之前只让内核儿帮他关心读事件。
    // 但是现在我想让他同时关心写事件。那么我只需要调用这个接口时
    // 将传入的参数bool readable 和 bool writeable都设置为true即可
    void EnableReadWrite(int sockfd, bool readable, bool writeable)
    {
        if (IsConnectionExists(sockfd))
        {
            
            // 按照用户的要求，构造出修改之后的事件位图
            uint32_t events = ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
            // 将sockfd对应连接的事件位图更新
            _connections[sockfd]->SetEvents(events);
            // 将我们的修改操作告诉epoll实例。
            _epoller->Update(sockfd, _connections[sockfd]->GetEvents());
        }
    }
    // 删除sockfd对应的连接
    void DelConnection(int sockfd)
    {
        // 这个过程主要要干三件事情。
        // 1. 从epoll实例中删除sockfd
        // 2. 调用close释放对应的struct file，同时也将fd_array[sockfd]中的内容清空
        // 3. 从_connections中删除sockfd对应的连接
        if (IsConnectionExists(sockfd))
        {
            // 1. 从内核中异常对sockfd的关心
            _epoller->Delete(sockfd);
            // 2. 关闭特定的文件描述
            _connections[sockfd]->Close();
            // 3. 从_connections中移除对应的connection
            _connections.erase(sockfd);
        }
    }

    // 对所有已经就绪的事件进行事件派发。其中参数n代表就绪事件的数量。
    void Dispatcher(int n)
    {
        for (int i = 0; i < n; i++)
        {
            // 开始进行派发, 派发给指定的模块
            int sockfd = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;
            if ((revents & EPOLLERR) || (revents & EPOLLHUP))
                revents = (EPOLLIN | EPOLLOUT); // 将异常事件，转换成为读写事件
            if ((revents & EPOLLIN) && IsConnectionExists(sockfd))
            {
                _connections[sockfd]->Recver();
            }
            if ((revents & EPOLLOUT) && IsConnectionExists(sockfd))
            {
                _connections[sockfd]->Sender();
            }
        }
    }

    void LoopOnce(int timeout)
    {
        int n = _epoller->Wait(_revs, event_num, timeout);
        Dispatcher(n);
    }
    void DebugPrint()
    {
        std::cout << "Epoller 管理的fd: ";
        for (auto &iter : _connections)
        {
            std::cout << iter.first << " ";
        }
        std::cout << std::endl;
    }
    void Loop()
    {
        _isrunning = true;
        // int timeout = -1;
        int timeout = 1000;
        while (_isrunning)
        {
            LoopOnce(timeout);
            DebugPrint();
            // 超时管理
            // 简单的,遍历_connections, 判断当前时间 - connection的最近访问时间>XXX
            // 超时了
        }
        _isrunning = false;
    }
    void Stop()
    {
        _isrunning = false;
    }
    ~Reactor()
    {
    }

private:
    std::unique_ptr<Epoller> _epoller;                  // 不要忘记初始化
    
    // 我们前面说reactor的作用就是负责连接的管理和派发，肯定就是要有一个连接队列用来存放所有的连接
    std::unordered_map<int, connection_t> _connections; // fd: Connection， 服务器内部所有的连接
    bool _isrunning;

    // 事件派发的数组，里面存放的是
    struct epoll_event _revs[event_num];
};